#include "Sincronizador.h"

void Sincronizador::delete_sync_folder( string path ){

    DIR *dir;
    struct dirent *ent;

    dir = opendir ( path.c_str() );

    if (dir != NULL) {
        path = path + "/";

        /* print all the files and directories within directory */
        while ((ent = readdir (dir)) != NULL) {
            if ( ent->d_type == DT_REG ) {
                string filename = path + string( ent->d_name );
				unlink( filename.c_str() );
            }
        }
        closedir (dir);
    } else {
        /* could not open directory */
        return;
    }
	rmdir( path.c_str() );
}

void Sincronizador::copy( string destination, string source )
{
    //cout << "copy from " << source << " to " << destination << endl;
    char buf[1048576];
    int nwritten;
    char * ptr;
    int len;
    FILE * dest;

    int src = open(source.c_str() ,O_RDONLY,0);

    if(  src  ) {
        dest = fopen( destination.c_str() , "w+" );
        while ((len = read( src, buf, sizeof(buf) )) > 0 ) {
            ptr = buf;
            while ( len > 0 ) {
                nwritten = fwrite(ptr, 1, len, dest );
                ptr += nwritten;
                len -= nwritten;
            }
        }
        fclose( dest );
        close( src );
    }
}

void Sincronizador::replicate_file( int server, string filename, string localfilename, string size, string hash ) {
    char buf[4096];
    int n;
    int intentos = 0;
    int max_intentos = 345;
    FILE * sync_file;
    int bytes_a_leer = atoi( size.c_str() );
    unsigned int restantes;
    int chunk;
 
    string hash_actual = "";
    copy( localfilename, SINCRONIZADOR_DATA_PATH + filename );
    hash_actual = hasher.GenerarHash( localfilename );
    while ( ( hash != hash_actual ) && ( intentos < max_intentos ) ) {

        unlink( localfilename.c_str() );
        send_message( server, filename );
        sync_file = fopen( localfilename.c_str() , "w+" );

        restantes = bytes_a_leer;
        while ( restantes > 0 ) {
                     //printf("restantes: %d\n", restantes );
            chunk = ( restantes > sizeof(buf) ) ? sizeof(buf) : restantes;

            //printf("chunk: %d\n", chunk );
            n = read(server, buf, chunk );

            //printf("leido: %d\n", n );
            fwrite(buf, 1, n, sync_file );
            restantes-=n;
        }

        fclose(sync_file);
        hash_actual = hasher.GenerarHash( localfilename );
              intentos++;
    }
    if (intentos >= max_intentos ) {
        cout << "Error Fuerte. Pasaje de " << filename << " fallo mas de " << max_intentos << " veces " << endl;
    }

}

void Sincronizador::cat( int client, string filename ) {
    char buf[4096];
	int nsent;
	char * ptr;
	int len;

    int resource = open(filename.c_str() ,O_RDONLY,0);

    //int flag = 1; 
    //setsockopt(client, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int));
    
    if(  resource  ) {
        while ((len = read(resource, buf, sizeof(buf) )) > 0 ) {
            ptr = buf;
            while ( len > 0 ) {

                //cout << "len: " << len << endl;
                nsent = send(client,ptr,len, 0);

                //cout << "nsent: " << nsent << endl;
                ptr += nsent;
                len -= nsent;
            }
        }
    }

    close( resource );
}

string Sincronizador::get_line( int sock ) {
    char buf[1024];
    memset(buf, 0, sizeof(buf));
    int size = 1024;
    int i = 0;
    char c = '\0';
    int n;

    while ((i < size - 1) && (c != '\n')) {
        n = recv(sock, &c, 1, 0);
        /* DEBUG printf("%02X\n", c); */
        if (n > 0) {
            if (c == '\r') {
                n = recv(sock, &c, 1, MSG_PEEK);
                /* DEBUG printf("%02X\n", c); */
                if ((n > 0) && (c == '\n'))
                    recv(sock, &c, 1, 0);
                else
                    c = '\n';
            }
            buf[i] = c;
            i++;
        } else
            c = '\n';
        }

    buf[i] = '\0';
    string line = string( buf );


    return line;
}

int Sincronizador::dir_exists( string path ) {
    struct stat sb;
    return (stat(path.c_str(), &sb) == 0 && S_ISDIR(sb.st_mode));
}

void Sincronizador::send_message( int sock, string message )
{
    char buf[8192];
    char * ptr;
    int len;
    int nsent;
    memset(buf, 0, sizeof(buf));

    stringstream s;
    s << message.length();
    string msg_len = s.str() + "\n";

    strcpy( buf, msg_len.c_str() );
    len = msg_len.length();

    ptr = buf;


    while ( len > 0 ) {
        nsent = send( sock, buf, len, 0 );

        ptr += nsent;
        len -= nsent;

    }
    memset(buf, 0, sizeof(buf));


    strcpy( buf, message.c_str() );
    len = message.length();

    ptr = buf;
    while ( len > 0 ) {
        nsent = send( sock, buf, len, 0 );

        ptr += nsent;
        len -= nsent;

    }

}

string Sincronizador::get_message( int sock ) {
    char buf[8192];
    memset(buf, 0, sizeof(buf));
    string line = get_line( sock );

    int bytes_a_leer = atoi( line.c_str() );



    int n;
    unsigned int restantes;
    int chunk;
    string message;

    restantes = bytes_a_leer;
    while ( restantes > 0 ) {
            chunk = ( restantes > sizeof(buf) ) ? sizeof(buf) : restantes;

            n = read(sock, buf, chunk );

            buf[ n ] = '\0';
            message += string( buf );
            memset(buf, 0, sizeof(buf));
            restantes-=n;

    }


    return message;
}

int Sincronizador::file_exists( string filename ) {

    FILE *fp = fopen( filename.c_str(), "r" );
    if( fp ) {
        fclose(fp);

        return 1;
    } else {

        return 0;
    }
}

string Sincronizador::get_file_last_mod( string filename ) {
    char t[ 100 ] = "";
    struct stat b;

    string fecha;
    if (!stat(filename.c_str(), &b)) {
        strftime(t, 100, "%Y.%m.%d.%H.%M.%S", localtime( &b.st_mtime));
        fecha = string( t );
    }


    return fecha;
}

string Sincronizador::get_file_size( string filename ) {
    struct stat b;

    stringstream s;

    string size;
    if (!stat(filename.c_str(), &b)) {
        s << b.st_size;
        size = s.str();
    }


    return size;
}

void Sincronizador::gen_sync_list( vector<string> * list, string path ) {

    DIR *dir;
    struct dirent *ent;

    dir = opendir ( path.c_str() );
    string separador = "|";

    if (dir != NULL) {
        path = path + "/";

        /* print all the files and directories within directory */
        while ((ent = readdir (dir)) != NULL) {
            if ( ent->d_type == DT_REG ) {
                string filename = string( ent->d_name );
                string date = get_file_last_mod( path + filename );
                string size = get_file_size( path + filename );
                string hash = hasher.GenerarHash( path + filename );
                string entry = filename + separador + size + separador + date + separador + hash;
                (*list).push_back( entry );
            }
        }
        closedir (dir);
    } else {
        /* could not open directory */
        return;
    }
}

vector<string> &Sincronizador::split(const string &s, char delim, vector<string> &elems) {
    stringstream ss(s);
    string item;
    while(getline(ss, item, delim)) {
        elems.push_back(item);
    }
    return elems;
}

vector<string> Sincronizador::split(const string &s, char delim) {
    vector<string> elems;
    return split(s, delim, elems);
}

int Sincronizador::ServidorExponerCarpeta( int cliente, string path ) {

    unsigned int i;
    stringstream s;
    string sync_list_size;
    vector<string> sync_list;

    gen_sync_list( &sync_list, path );

    string serialized_list;

    for (i = 0; i < sync_list.size(); i++) 
        serialized_list += sync_list[i] + "\n";

    send_message( cliente, serialized_list );

    string respuesta;

    for (respuesta = ""; respuesta != "SYNC_END" ; respuesta = get_message( cliente ) ) {
        if (respuesta != "") {
            string full_path = path + "/" + respuesta;
            cat( cliente, full_path );
        }
    }
    return 0;
}

int Sincronizador::ClienteSincronizarConServidor( int server, string path ) {
    char separador = '|';
    char nl = '\n';
    unsigned int i;
    //FILE * sync_file;
    stringstream s;
    path += "/";

    string serialized_list = get_message( server );
    vector<string> sync_list = split( serialized_list, nl );

    if ( dir_exists( path ) ) {
       
        for (i = 0; i < sync_list.size(); i++) {
            vector<string> info = split( sync_list[i], separador );
            string file_in_path = path + info[0];
            if ( file_exists( file_in_path ) ) {
                string last_mod = get_file_last_mod( file_in_path );
                if ( last_mod < info[2] ) {
                    replicate_file( server, info[0], file_in_path, info[1], info[3] );
                }
            } else {
                replicate_file( server, info[0], file_in_path, info[1], info[3] );
            }
        }
    } else {
        mkdir( path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH );
        for (i = 0; i < sync_list.size(); i++) {
            vector<string> info = split( sync_list[i], separador );
            string file_in_path = path + info[0];
            replicate_file( server, info[0], file_in_path, info[1], info[3] );
        }
    }
    
    string sync_end = "SYNC_END";
    send_message( server, sync_end );
    return 0;

}

